<template>
  <div>
    <el-upload
      class="upload-demo"
      :on-preview="handlePreview"
      :on-remove="handleRemove"
      :before-remove="beforeRemove"
      :headers="myHeaders"
      :on-exceed="handleExceed"
      :file-list="fileList"
      :limit="limit"
      :before-upload="beforeAvatarUpload"
      v-bind="$attrs"
      :http-request="uploadLogin"
    >
      <slot name="button"></slot>
      <div slot="tip" class="el-upload__tip">
        <slot name="tipTest"></slot>
      </div>
      <div v-if="type === 'images'" slot="file" slot-scope="{ file }">
        <img
          class="el-upload-list__item-thumbnail"
          :src="baseUrl + file.url"
          alt=""
        />
        <span class="el-upload-list__item-actions">
          <span class="el-upload-list__item-delete" @click="handleRemove(file)">
            <i class="el-icon-delete"></i>
          </span>
        </span>
      </div>
    </el-upload>
    <el-progress
      v-if="showProgress && type !== 'images'"
      :percentage="uploadProgress"
    />
  </div>
</template>

<script>
import setting from '@/config/setting'
import md5 from 'js-md5' //引入MD5加密

export default {
  model: {
    prop: 'value',
    event: 'input'
  },
  props: {
    type: {
      type: String,
      default() {
        return 'file'
      }
    },
    value: {
      type: [String, Array, File, Blob],
      default() {
        return []
      }
    },
    fileData: {
      type: Object,
      default() {
        return {
          save_name: true,
          file_name: null,
          file_size: null,
          file_type: null
        }
      }
    },
    shardUpload: {
      type: Boolean,
      default() {
        return false
      }
    },
    chunkSize: {
      type: Number,
      default() {
        return 10
      }
    },
    limit: {
      type: Number,
      default() {
        return 1
      }
    },
    //默认最大10m
    MaxSize: {
      type: Number,
      default() {
        return 10
      }
    }
  },
  watch: {
    fileList: {
      deep: true,
      handler: function(val) {
        if (val.length > 0) {
          if (this.limit == 1) {
            this.$emit('input', val[0].url)
          } else {
            let arr = []
            val.forEach((item) => {
              this.$set(arr, arr.length, {
                url: item.url
              })
            })
            this.$emit('input', arr)
          }
        }
      }
    },
    fileListCopy: {
      deep: true,
      handler: function(val) {
        if (this.limit === 1) {
          this.$emit('getFile', val[0])
        } else {
          this.$emit('getFile', val)
        }
      }
    },
    value: {
      immediate: true,
      deep: true,
      handler: function(val) {
        if (typeof this.value == 'string') {
          if (this.value == '') {
            this.fileList = []
          } else {
            this.fileList = [
              {
                url: val,
                name: val
              }
            ]
          }
        } else if (this.value instanceof Array) {
          this.value.forEach((item) => {
            this.$set(this.fileList, this.fileList.length, {
              url: item,
              name: item
            })
          })
        } else {
          try {
            let fileReader = new FileReader()
            fileReader.readAsDataURL(this.value)
            fileReader.onload = (e) => {
              this.fileList = [e.target.result]
            }
          } catch (e) {
            console.log(e.message)
          }
        }
      }
    }
  },
  data() {
    let token = setting.takeToken()
    return {
      baseUrl: process.env.VUE_APP_API_BASE_URL,
      currentChunk: 0, // 当前分片数
      showProgress: false, // 是否显示进度条
      uploadProgress: 0, // 上传进度
      myHeaders: { Authorization: token },
      fileListCopy: [],
      fileList: []
    }
  },
  created() {
    this.baseUrl = this.baseUrl.slice(0, this.baseUrl.length - 1)
  },
  methods: {
    uploadLogin(file) {
      if (this.shardUpload) {
        this.uploadChunk(file)
      } else {
        this.upload(file)
      }
    },
    async uploadChunk(file) {
      if (file == 'false') {
        window.location.reload()
        return
      } else if (file == 'none') {
        return
      }
      const totalSize = file.file.size
      const chunkSize = this.chunkSize * 1024 * 1024 // 10MB
      const chunkCount = Math.ceil(totalSize / chunkSize)

      this.showProgress = true
      this.uploadProgress = 0
      await this.uploadChunkRecursive(0, file, chunkSize, totalSize, chunkCount)
    },
    upload(file) {
      let _this = this
      const formData = new FormData()
      formData.append('file', file.file)
      this.$http.post(this.$attrs.action, formData).then(async (res) => {
        if (res.data.code === 0) {
          let { save_name, file_name, file_size, file_type } = res.data.data
          console.log(_this.fileData['file_name'])
          let obj = {}
          for (let key in res.data.data) {
            if (_this.fileData[key]) {
              obj[key] = res.data.data[key]
            }
          }
          this.$set(this.fileListCopy, this.fileListCopy.length, obj)
          this.$set(this.fileList, this.fileList.length, {
            name: _this.fileData['file_name'] ? file_name : save_name,
            url: save_name,
            file_size: file_size,
            file_type: file_type
          })
        }
      })
    },
    async uploadChunkRecursive(index, file, chunkSize, totalSize, chunkCount) {
      if (index >= chunkCount) {
        return
      }
      // 更新当前分片数和总分片数
      this.currentChunk = index + 1
      const start = index * chunkSize
      const end = Math.min(start + chunkSize, totalSize)
      const chunk = file.file.slice(start, end)
      const chunkMD5 = await this.readChunkMD5(chunk)
      const formData = new FormData()
      formData.append('file', chunk)
      formData.append('chunkNumber', index + 1)
      formData.append('chunkSize', chunkSize)
      formData.append('currentChunkSize', chunk.size)
      formData.append('filename', file.file.name)
      formData.append('totalChunks', chunkCount)
      formData.append('md5', chunkMD5)
      this.$http.post(this.$attrs.action, formData).then(async (res) => {
        if (res.data.code === 2) {
          this.uploadProgress = Math.round(
            (this.currentChunk / chunkCount) * 100
          )
          this.$set(this.fileList, this.fileList.length, {
            name: res.data.file_path,
            url: res.data.file_path
          })
          this.showProgress = false
          this.uploadProgress = 0
          this.$message.success(res.data.msg)
        } else if (res.data.code === 1) {
          this.uploadProgress = Math.round(
            (this.currentChunk / chunkCount) * 100
          )
          await this.uploadChunkRecursive(
            index + 1,
            file,
            chunkSize,
            totalSize,
            chunkCount
          )
        }
      })
    },
    async readChunkMD5(chunk) {
      return new Promise((resolve) => {
        let fileReaderInstance = new FileReader()
        fileReaderInstance.readAsBinaryString(chunk)
        fileReaderInstance.addEventListener('load', (e) => {
          let fileBlob = e.target.result
          let chunkMD5 = md5(fileBlob)
          resolve(chunkMD5)
        })
      })
    },
    handleRemove(file, fileList) {
      console.log(file, fileList)
      if (this.limit == 1) {
        this.fileListCopy = []
        this.$emit('input', '')
      } else {
        if (this.shardUpload) {
          this.$emit('input', this.fileList)
        } else {
          this.fileListCopy = this.fileListCopy.filter((item) => {
            return item.save_name !== file.url
          })
          this.$emit('input', this.fileList)
        }
      }
    },
    handlePreview(file) {
      console.log(file)
    },
    handleExceed() {
      this.$message.warning(`当前限制选择 ${this.limit} 个文件`)
    },
    beforeRemove(file) {
      return this.$confirm(`确定移除 ${file.name}？`)
    },
    beforeAvatarUpload(file) {
      const isLt2M = file.size / 1024 / 1024 < this.MaxSize
      if (!isLt2M) {
        this.$message.error(`上传文件大小不能超过 ${this.MaxSize}MB!`)
      }
      return isLt2M
    }
  }
}
</script>

<style lang="scss" scoped>
::v-deep .el-upload-list__item-thumbnail {
  width: 100%;
}
</style>
